package org.zjt.chapter1;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.Asserts;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * Description:
 *
 * @author juntao.zhang
 * Date: 2018-06-29 上午10:25
 * @see
 */
@Slf4j
public class Chapter1Client {

    public static String HOST = "localhost";

    public static Integer PORT = 9090;
    public static void main(String[] args) {

        // Configure the client.
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new TimeClientHandler());
                        }
                    });

            // Start the client.
            ChannelFuture f = bootstrap.connect(HOST, PORT)
                    .addListener(future -> log.info(future.isSuccess()? "连接成功" :"连接失败"))
                    .sync();

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // Shut down the event loop to terminate all threads.
            group.shutdownGracefully();
        }
    }



    @Slf4j
    public static class TimeClientHandler extends ChannelInboundHandlerAdapter {


        private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);

        /**
         * Creates a client-side handler.
         */
        public TimeClientHandler() {
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            Optional<ByteBuf> msgOptional = string2ByteBuf("hello world");
            if (msgOptional.isPresent())
            ctx.writeAndFlush(msgOptional.get());
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            Asserts.check(msg instanceof ByteBuf,"msg is not ByteBuf.class instance ");


            ByteBuf byteBuf = ByteBuf.class.cast(msg);
            Optional<String> stringOptional = byteBuf2String(byteBuf);
            if (stringOptional.isPresent()){
                String msgStr = stringOptional.get();
                log.info("msgStr:{}",msgStr);
                byteBufLoadString(byteBuf,msgStr + ATOMIC_INTEGER.incrementAndGet());
                ctx.write(byteBuf);
            }
        }

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            ctx.flush();
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            // Close the connection when an exception is raised.
            cause.printStackTrace();
            ctx.close();
        }
    }


    public static Optional<String> byteBuf2String(ByteBuf byteBuf) {
        byte[] byteArray = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(byteArray);
        return Optional.ofNullable(new String(byteArray));
    }


    public static void byteBufLoadString(ByteBuf byteBuf,String msg) {
        Objects.requireNonNull(byteBuf,"byteBuf is not null");
        Objects.requireNonNull(msg,"msg is not null");

        byteBuf.resetReaderIndex();
        byteBuf.resetWriterIndex();
        byteBuf.writeBytes(msg.getBytes());
    }


    public static Optional<ByteBuf> string2ByteBuf(String msg) {
        return Optional.ofNullable(Unpooled.wrappedBuffer(msg.getBytes()));
    }
}
